﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using Slam.Configuration;
using Slam.Data;
using Slam.Logging;
using Slam.SharePoint;
using Microsoft.SharePoint;

namespace Slam.Events
{
	internal static class DefaultEvents
	{
		internal static bool Create(SPListItem listItem, IDataServices dataServices, IConfigurationManager configuration, ILogger logger, string eventPrefix)
		{
			SPWeb site = listItem.Web;
			bool isInError = false;
			SPList parentList = listItem.ParentList;

			try
			{
				string typeName = SlamItemContext.TypeName;
				string logItemInformation = typeName + Environment.NewLine + " List: " + parentList.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString();
				logger.LogMessage("Information", eventPrefix + " Start - " + logItemInformation);

				string itemTypeName = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName, "TableName");
				if (String.IsNullOrEmpty(itemTypeName))
				{
					itemTypeName = typeName;
				}

				List<Field> outputFields = configuration.DataMapping.GetTypeFields(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName).ToList();
				Hashtable values = new Hashtable();
				try
				{
					values = listItem.GetFieldValues(outputFields);
					dataServices.Insert(itemTypeName, values);
				}
				catch (SqlException)
				{
					logger.LogMessage("Information", eventPrefix + " Failed, re-slamming associated items and trying again - " + logItemInformation);
					listItem.EnsureSlamAssociations(outputFields, true);
					values = listItem.GetFieldValues(outputFields);
					dataServices.Insert(itemTypeName, values);
				}

				logger.LogMessage("Information", eventPrefix + " Complete - " + logItemInformation);

				logger.LogMessage("Information", eventPrefix + " Associations - " + logItemInformation);

				IEnumerable<AssociationField> associationFields = configuration.DataMapping.GetTypeAssociations(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName);
				listItem.EnsureSlamAssociations(associationFields.Cast<Field>().ToList(), false);

				foreach (AssociationField association in associationFields)
				{
					if (!String.IsNullOrEmpty(association.AssociationName) && association is SqlMappedAssociationField)
					{
						SqlMappedAssociationField sqlAssociationField = (SqlMappedAssociationField)association;
						foreach (SPListItem associatedItem in listItem.GetLookupValueListItems(association.Name))
						{
							logger.LogMessage("Information", eventPrefix + " Association - Type: " + association.AssociationName + " From: " + itemTypeName + " To: " + association.AssociatedTypeName);
							try
							{
								dataServices.Associate(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, listItem.GetUniqueId().ToString(), sqlAssociationField.SecondAssociatedTypeIdColumn, associatedItem.GetUniqueId().ToString());
							}
							catch
							{
								listItem.EnsureSlamAssociations(associationFields.Cast<Field>().ToList(), true);
								dataServices.Associate(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, listItem.GetUniqueId().ToString(), sqlAssociationField.SecondAssociatedTypeIdColumn, associatedItem.GetUniqueId().ToString());
							}
						}
					}
				}
				logger.LogMessage("Information", eventPrefix + " Associations Complete - " + logItemInformation);

				string attachmentTable = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName, "AttachmentTableName");

				if (!String.IsNullOrEmpty(attachmentTable))
				{
					logger.LogMessage("Information", eventPrefix + " Attachments - " + logItemInformation);

					Field idField = outputFields[0];
					string idColumnName = idField.Name;
					if (idField is SqlMappedField)
					{
						idColumnName = String.IsNullOrEmpty(((SqlMappedField)idField).Column) ? idColumnName : ((SqlMappedField)idField).Column;
					}

					string baseAttachmentUrl = listItem.Attachments.UrlPrefix.Replace(site.Url, "");
					foreach (string attachmentFileUrl in listItem.Attachments)
					{
						Hashtable attachmentValues = new Hashtable();
						attachmentValues.Add(idColumnName, values[idColumnName]);
						attachmentValues.Add("FileUrl", baseAttachmentUrl + attachmentFileUrl);
						dataServices.Insert(attachmentTable, attachmentValues, new object[] { true, itemTypeName });
					}

					logger.LogMessage("Information", eventPrefix + " Attachments Complete - " + logItemInformation);
				}
			}
			catch (Exception ex)
			{
				logger.LogMessage("Exception", ex.ToString());
				isInError = true;
			}

			logger.LogEvent("Create", SlamItemContext.TypeName, "List: " + parentList.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString());
			return !isInError;
		}

		internal static bool Delete(SPListItem listItem, IDataServices dataServices, IConfigurationManager configuration, ILogger logger, string eventPrefix)
		{
			SPWeb site = listItem.Web;
			bool isInError = false;
			SPList parentList = listItem.ParentList;

			try
			{
				string typeName = SlamItemContext.TypeName;
				string logItemInformation = typeName + Environment.NewLine + " List: " + parentList.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString();
				
				string itemTypeName = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName, "TableName");
				if (String.IsNullOrEmpty(itemTypeName))
				{
					itemTypeName = typeName;
				}

				List<string> clearedAssociations = new List<string>();

				logger.LogMessage("Information", eventPrefix + " Associations - " + logItemInformation);

				foreach (AssociationField association in configuration.DataMapping.GetTypeAssociations(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName))
				{
					if (!String.IsNullOrEmpty(association.AssociationName) && association is SqlMappedAssociationField && listItem.Fields.Cast<SPField>().Where(f => f.Title.Equals(association.Name, StringComparison.CurrentCultureIgnoreCase) || f.InternalName.Equals(association.Name, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault() != null)
					{
						SqlMappedAssociationField sqlAssociationField = (SqlMappedAssociationField)association;
					
						if (!clearedAssociations.Contains(association.AssociationName))
						{
							logger.LogMessage("Information", eventPrefix + " Associations - For: " + itemTypeName + " Type: " + association.AssociationName);
							dataServices.DeleteAllAssociations(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, listItem.GetUniqueId().ToString());
							clearedAssociations.Add(association.AssociationName);
						}
					}
				}
				logger.LogMessage("Information", eventPrefix + " Associations Complete - " + logItemInformation);

				string attachmentTable = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName, "AttachmentTableName");

				if (!String.IsNullOrEmpty(attachmentTable))
				{
					logger.LogMessage("Information", eventPrefix + " Attachments - " + logItemInformation);

					List<Field> outputFields = configuration.DataMapping.GetTypeFields(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName).ToList();
					Field idField = outputFields[0];
					string idColumnName = idField.Name;
					if (idField is SqlMappedField)
					{
						idColumnName = String.IsNullOrEmpty(((SqlMappedField)idField).Column) ? idColumnName : ((SqlMappedField)idField).Column;
					}

					dataServices.Delete(attachmentTable, Convert.ToString(listItem.GetUniqueId()), new object[] { true, idColumnName });

					logger.LogMessage("Information", eventPrefix + " Attachments Complete - " + logItemInformation);
				}

				logger.LogMessage("Information", eventPrefix + " Start - " + logItemInformation);

				dataServices.Delete(itemTypeName, Convert.ToString(listItem.GetUniqueId()));

				logger.LogMessage("Information", eventPrefix + " Complete - " + logItemInformation);


			}
			catch (Exception ex)
			{
				logger.LogMessage("Exception", ex.ToString());
				isInError = true;
			}

			logger.LogEvent("Delete", SlamItemContext.TypeName, "List: " + parentList.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString());

			return !isInError;
		}

		internal static bool Update(SPListItem listItem, IDataServices dataServices, IConfigurationManager configuration, ILogger logger, string eventPrefix)
		{
			SPWeb site = listItem.Web;
			bool isInError = false;
			SPList parentList = listItem.ParentList;

			try
			{
				string typeName = SlamItemContext.TypeName;
				string logItemInformation = typeName + Environment.NewLine + " List: " + parentList.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString();
				logger.LogMessage("Information", eventPrefix + " Start - " + logItemInformation);

				string itemTypeName = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName, "TableName");
				if (String.IsNullOrEmpty(itemTypeName))
				{
					itemTypeName = typeName;
				}
				
				List<Field> outputFields = configuration.DataMapping.GetTypeFields(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName).ToList();
				Hashtable values = new Hashtable();
				try
				{
					values = listItem.GetFieldValues(outputFields);
					dataServices.Update(itemTypeName, listItem.GetUniqueId().ToString(), values);
				}
				catch (SqlException)
				{
					logger.LogMessage("Information", eventPrefix + " Failed, re-slamming associated items and trying again - " + logItemInformation);
					listItem.EnsureSlamAssociations(outputFields, true);
					values = listItem.GetFieldValues(outputFields);
					dataServices.Update(itemTypeName, listItem.GetUniqueId().ToString(), values);
				}

				logger.LogMessage("Information", eventPrefix + " Complete - " + logItemInformation);

				List<string> clearedAssociations = new List<string>();

				logger.LogMessage("Information", eventPrefix + " Associations - " + logItemInformation);

				IEnumerable<AssociationField> associationFields = configuration.DataMapping.GetTypeAssociations(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName);
				listItem.EnsureSlamAssociations(associationFields.Cast<Field>().ToList(), false);

				foreach (AssociationField association in associationFields)
				{
					if (!String.IsNullOrEmpty(association.AssociationName) && association is SqlMappedAssociationField)
					{
						SqlMappedAssociationField sqlAssociationField = (SqlMappedAssociationField)association;
						if (!clearedAssociations.Contains(association.AssociationName))
						{
							logger.LogMessage("Information", eventPrefix + " Delete Associations - For: " + itemTypeName + " Type: " + association.AssociationName);
							dataServices.DeleteAllAssociations(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, listItem.GetUniqueId().ToString());
							clearedAssociations.Add(association.AssociationName);
						}
						foreach (SPListItem associatedItem in listItem.GetLookupValueListItems(association.Name))
						{
							if (associatedItem.VerifyItemRequiresSlamUpdate())
							{
								associatedItem.SlamUpdate();
							}
							logger.LogMessage("Information", eventPrefix + " Association - Type: " + association.AssociationName + " From: " + itemTypeName + " To: " + association.AssociatedTypeName);
							try
							{
								dataServices.Associate(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, listItem.GetUniqueId().ToString(), sqlAssociationField.SecondAssociatedTypeIdColumn, associatedItem.GetUniqueId().ToString());
							}
							catch
							{
								logger.LogMessage("Information", eventPrefix + " Association failed, re-slamming associated items and trying again - Type: " + association.AssociationName + " From: " + itemTypeName + " To: " + association.AssociatedTypeName);
								listItem.EnsureSlamAssociations(associationFields.Cast<Field>().ToList(), true);
								dataServices.Associate(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, listItem.GetUniqueId().ToString(), sqlAssociationField.SecondAssociatedTypeIdColumn, associatedItem.GetUniqueId().ToString());
							}
						}
					}
				}
				logger.LogMessage("Information", eventPrefix + " Associations Complete - " + logItemInformation);

				string attachmentTable = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName, "AttachmentTableName");

				if (!String.IsNullOrEmpty(attachmentTable))
				{
					logger.LogMessage("Information", eventPrefix + " Attachments - " + logItemInformation);

					Field idField = outputFields[0];
					string idColumnName = idField.Name;
					if (idField is SqlMappedField)
					{
						idColumnName = String.IsNullOrEmpty(((SqlMappedField)idField).Column) ? idColumnName : ((SqlMappedField)idField).Column;
					}

					logger.LogMessage("Information", eventPrefix + " Delete Attachments - " + logItemInformation);
					dataServices.Delete(attachmentTable, Convert.ToString(values[idColumnName]), new object[] { true, idColumnName });

					string baseAttachmentUrl = listItem.Attachments.UrlPrefix.Replace(site.Url, "");
					foreach (string attachmentFileUrl in listItem.Attachments)
					{
						Hashtable attachmentValues = new Hashtable();
						attachmentValues.Add(idColumnName, values[idColumnName]);
						attachmentValues.Add("FileUrl", baseAttachmentUrl + attachmentFileUrl);
						dataServices.Insert(attachmentTable, attachmentValues, new object[] { true, itemTypeName });
					}

					logger.LogMessage("Information", eventPrefix + " Attachments Complete - " + logItemInformation);
				}
			}
			catch (Exception ex)
			{
				logger.LogMessage("Exception", ex.ToString());
				isInError = true;
			}

			logger.LogEvent("Update", SlamItemContext.TypeName, "List: " + parentList.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString());

			return !isInError;
		}
	}
}
